package com.ttg.common.utils;

import sun.misc.BASE64Decoder;

import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

public class RsaUtils {

    private static final Base64.Encoder ENCODER = Base64.getEncoder();

    private static final Base64.Decoder DECODER = Base64.getDecoder();
    private static String KEY_ALGORITHM = "RSA/ECB/PKCS1Padding";

    private static final String ALGORITHM = "RSA";
    private static final String SHA1WITHRSA = "SHA1withRSA";

    private static final int KEY_SIZE = 2048;
    /**
     * 生成公钥map的key
     */
    public static final String MAP_KEY_PUBLICKEY = "publicKey";
    /**
     * 生成私钥map的key
     */
    public static final String MAP_KEY_PRIVATEKEY = "privateKey";

    /**
     * 生成RSA密钥对
     *
     * @param seed 随机数种子
     * @return 密钥对
     * @throws NoSuchAlgorithmException 异常
     */
    public static KeyPair generateKeyPair(String seed) throws NoSuchAlgorithmException {

        // RSA算法要求有一个可信任的随机数源
        SecureRandom secureRandom = new SecureRandom(seed.getBytes());

        // 为RSA算法创建一个KeyPairGenerator对象
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(ALGORITHM);

        // 利用上面的随机数据源初始化这个KeyPairGenerator对象
        keyPairGenerator.initialize(KEY_SIZE, secureRandom);

        // 生成密匙对
        return keyPairGenerator.generateKeyPair();
    }

    /**
     * 生成RSA密钥对
     *
     * @return 密钥对
     * @throws NoSuchAlgorithmException 异常
     */
    public static KeyPair generateKeyPair() throws NoSuchAlgorithmException {

        // RSA算法要求有一个可信任的随机数源
        SecureRandom secureRandom = new SecureRandom();

        // 为RSA算法创建一个KeyPairGenerator对象
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(ALGORITHM);

        // 利用上面的随机数据源初始化这个KeyPairGenerator对象
        keyPairGenerator.initialize(KEY_SIZE, secureRandom);

        // 生成密匙对
        return keyPairGenerator.generateKeyPair();
    }

    /**
     * 生成base64编码后的RSA密钥对
     *
     * @return base64编码后的RSA密钥对
     * @throws NoSuchAlgorithmException 异常
     */
    public static Map<String, String> generateBase64EncodedKeyPair() throws NoSuchAlgorithmException {
        KeyPair keyPair = generateKeyPair();
        //得到公钥
        Key publicKey = keyPair.getPublic();
        //得到私钥
        Key privateKey = keyPair.getPrivate();
        //获取公私钥编码值
        byte[] publicKeyBytes = publicKey.getEncoded();
        byte[] privateKeyBytes = privateKey.getEncoded();
        //base64编码
        String publicKeyBase64Encoded = ENCODER.encodeToString(publicKeyBytes);
        String privateKeyBase64Encoded = ENCODER.encodeToString(privateKeyBytes);
        Map<String, String> base64EncodedKeyPair = new HashMap<>();
        base64EncodedKeyPair.put(MAP_KEY_PUBLICKEY, publicKeyBase64Encoded);
        base64EncodedKeyPair.put(MAP_KEY_PRIVATEKEY, privateKeyBase64Encoded);
        return base64EncodedKeyPair;
    }

    /**
     * Base64编码的公钥还原为公钥对象
     *
     * @param base64EncodedPublicKey 密钥字符串（经过base64编码）
     * @throws GeneralSecurityException 异常
     */
    public static PublicKey getPublicKey(String base64EncodedPublicKey) throws GeneralSecurityException {
        byte[] keyBytes;
        keyBytes = DECODER.decode(base64EncodedPublicKey);
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
        return keyFactory.generatePublic(keySpec);
    }

    /**
     * 公钥还原为公钥对象
     *
     * @param keyBytes byte[]
     * @throws GeneralSecurityException 异常
     */
    public static PublicKey getPublicKey(byte[] keyBytes) throws GeneralSecurityException {
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
        return keyFactory.generatePublic(keySpec);
    }

    /**
     * Base64编码的私钥还原为私钥对象
     *
     * @param base64EncodedPrivateKey 密钥字符串
     * @throws GeneralSecurityException 异常
     */
    public static PrivateKey getPrivateKey(String base64EncodedPrivateKey) throws GeneralSecurityException {
        byte[] keyBytes;
        keyBytes = DECODER.decode(base64EncodedPrivateKey);
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
        return keyFactory.generatePrivate(keySpec);
    }

    /**
     * 私钥还原为私钥对象
     *
     * @param keyBytes byte[]
     * @throws GeneralSecurityException 异常
     */
    public static PrivateKey getPrivateKey(byte[] keyBytes) throws GeneralSecurityException {
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
        return keyFactory.generatePrivate(keySpec);
    }

    /**
     * 将java格式公钥转换为非java格式公钥字符串
     */
    public static String getNonJavaPublicKey(String publicKeyBase64Encoded) throws Exception {
        return "-----BEGIN PUBLIC KEY-----\n" + getStringByEnter(64, publicKeyBase64Encoded) + "\n-----END PUBLIC KEY-----";
    }

    /**
     * 将java格式私钥转换为非java格式私钥字符串
     */
    public static String getNonJavaprivateKey(String privateKeyBase64Encoded) throws Exception {
        return "-----BEGIN PRIVATE KEY-----\n" + getStringByEnter(64, privateKeyBase64Encoded) + "\n-----END PRIVATE KEY-----";
    }

    private static String getStringByEnter(int length, String string) throws Exception {
        for (int i = 1; i <= string.length(); i++) {
            if (string.substring(0, i).getBytes("GBK").length > length) {
                return string.substring(0, i - 1) + "\n" +
                        getStringByEnter(length, string.substring(i - 1));
            }
        }
        return string;
    }

    /**
     * 将非java格式公钥转换为java格式公钥对象
     */
    public static PublicKey ConvertPublicKey(String NonJavaPublicKey) throws GeneralSecurityException {
        String publicKeyStr = NonJavaPublicKey
                .replace("-----BEGIN PUBLIC KEY-----", "")
                .replace("-----END PUBLIC KEY-----", "")
                .replaceAll("\\r", "")
                .replaceAll("\\n", "")
                .replaceAll("\\s+", "");
        return getPublicKey(publicKeyStr);
    }

    /**
     * 将非java格式私钥转换为java格式私钥对象
     */
    public static PrivateKey ConvertPrivateKey(String NonJavaPrivateKey) throws GeneralSecurityException {
        String privateKeyStr = NonJavaPrivateKey
                .replace("-----BEGIN PRIVATE KEY-----", "")
                .replace("-----END PRIVATE KEY-----", "")
                .replaceAll("\\r", "")
                .replaceAll("\\n", "")
                .replaceAll("\\s+", "");
        return getPrivateKey(privateKeyStr);
    }

    /**
     * 微信平台证书转化成数据库保存的格式
     */
    public static String convertCertificate(String certificate) throws GeneralSecurityException {
        return certificate
                .replace("-----BEGIN CERTIFICATE-----", "")
                .replace("-----END CERTIFICATE-----", "")
                .replaceAll("\\r", "")
                .replaceAll("\\n", "")
                .replaceAll("\\s+", "");
    }

    /**
     * RSA公钥加密
     */
    public static byte[] rsaPublicEncrypt(byte[] plaintext, PublicKey publicKey) throws GeneralSecurityException {
        Cipher cipher = Cipher.getInstance(ALGORITHM);
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        return cipher.doFinal(plaintext);
    }


    /**
     * RSA公钥加密
     */
    public static byte[] encryptByPublicKey(byte[] data, String publicKey) throws Exception {
        //byte[] keyBytes = new BASE64Decoder().decodeBuffer(publicKey);
        byte[] keyBytes = DECODER.decode(publicKey);
                X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PublicKey publicK = keyFactory.generatePublic(x509KeySpec);
        Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
        cipher.init(Cipher.ENCRYPT_MODE, publicK);
        return cipher.doFinal(data);
    }
    public static String encodePubRsa(String prikeyvalue, String signStr) {
        try {
            return ENCODER.encodeToString(encryptByPublicKey(signStr.getBytes(), prikeyvalue));
            //return new BASE64Encoder().encode(encryptByPublicKey(sign_str.getBytes(), prikeyvalue));
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return null;
    }

    /**
     * RSA私钥加密
     */
    public static byte[] rsaPrivateEncrypt(byte[] plaintext, PrivateKey privateKey) throws GeneralSecurityException {
        int MAX_ENCRYPT_BLOCK = 128;
        Cipher cipher = Cipher.getInstance(ALGORITHM);
        cipher.init(Cipher.ENCRYPT_MODE, privateKey);

        int inputLen = plaintext.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offSet = 0;
        byte[] cache;
        int i = 0;
        //  对数据分段加密
        while (inputLen - offSet > 0) {
            if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {
                cache = cipher.doFinal(plaintext, offSet, MAX_ENCRYPT_BLOCK);
            } else {
                cache = cipher.doFinal(plaintext, offSet, inputLen - offSet);
            }
            out.write(cache, 0, cache.length);
            i++;
            offSet = i * MAX_ENCRYPT_BLOCK;
        }
        byte[] encryptedData = out.toByteArray();
        return cipher.doFinal(plaintext);
    }


        /**
         * RSA公钥解密
         */
        public static byte[] rsaPublicDecrypt ( byte[] ciphertext, PublicKey publicKey) throws GeneralSecurityException
        {
            Cipher cipher = Cipher.getInstance(ALGORITHM);
            cipher.init(Cipher.DECRYPT_MODE, publicKey);
            return cipher.doFinal(ciphertext);
        }

        /**
         * RSA私钥解密
         */
        public static byte[] rsaPrivateDecrypt ( byte[] ciphertext, PrivateKey privateKey) throws
        GeneralSecurityException {
            Cipher cipher = Cipher.getInstance(ALGORITHM);
            cipher.init(Cipher.DECRYPT_MODE, privateKey);
            return cipher.doFinal(ciphertext);
        }

        /**
         * RSA私钥加密  +  byte2hex
         */
        public static String encryptByPrivateKey (String encryptStr, PrivateKey privateKey) throws
        GeneralSecurityException {
            byte[] plaintext = encryptStr.getBytes(StandardCharsets.UTF_8);
            byte[] EncryptedStr = rsaPrivateEncrypt(plaintext, privateKey);
            return BasicUtils.byte2hex(EncryptedStr);
        }

        /**
         * rsa生成签名
         */
        public static String rsaSignByPrivateKey (String encryptStr, String privateKey) throws Exception {
            PrivateKey privateKey1 = RsaUtils.ConvertPrivateKey(privateKey);
            byte[] plaintext = encryptStr.getBytes(StandardCharsets.UTF_8);
            Signature signature = Signature.getInstance(SHA1WITHRSA);
            signature.initSign(privateKey1);
            signature.update(plaintext);
            return BasicUtils.byte2hex(signature.sign());
        }

    public static boolean verify(byte[] data, String publicKey, String sign)
            throws Exception {
        byte[] keyBytes = new BASE64Decoder().decodeBuffer(publicKey);

        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);

        KeyFactory keyFactory = KeyFactory.getInstance("RSA");

        PublicKey pubKey = keyFactory.generatePublic(keySpec);
        Signature signature = Signature.getInstance("SHA1withRSA");
        signature.initVerify(pubKey);
        signature.update(data);

        //sign 16进制转换成byte数组
        return signature.verify(hexStrToBytes(sign));
    }

    public static final byte[] hexStrToBytes(String s) {

        byte[] bytes;

        bytes = new byte[s.length() / 2];

        for (int i = 0; i < bytes.length; i++) {
            bytes[i] = (byte) Integer.parseInt(s.substring(2 * i, 2 * i + 2), 16);
        }

        return bytes;
    }
}
